package com.indusblue.media.video
{
	import com.indusblue.events.VideoPlayerEvent;
	
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.NetStatusEvent;
	import flash.media.SoundTransform;
	import flash.media.Video;
	import flash.net.NetConnection;
	import flash.net.NetStream;

	public class VideoPlayer extends Sprite implements IVideoPlayer
	{	
		public static const PLAYING:String = "playing";
		public static const STOPPED:String = "stopped";
		public static const PAUSED:String = "paused";
					
		/******************************************************************************************
		 * PRIVATE VARIABLES
		 *****************************************************************************************/
		
		private var _connection: NetConnection;
		private var _stream:NetStream;
		private var _video:Video;	
		private var _lockAspectRatio:Boolean;
		private var _autoPlay: Boolean;
		private var _duration: Number;
		private var _position: Number;
		private var _muted: Boolean;
		private var _muteVolume: Number;
		private var _title: String;
		private var _state: String;
		private var _loop:Boolean;
		private var _metaDataFired:Boolean = false;
		
		
		/******************************************************************************************
		 * SETTERS AND GETTERS
		 *****************************************************************************************/
		
		
		/**
		 * Sets the width of the video player
		 * 
		 * @param 	width	The desired width of the player
		 *  	
		 **/
		override public function set width(width:Number):void
		{
			if(_lockAspectRatio)
			{
				var ratio:Number = _video.width / width;				
				_video.width /= ratio;	
				_video.height /= ratio;
			}
			else {
				
				_video.width = width;
			}
		}	
		
		
		/**
		 * Returns the width of the video player
		 **/ 
		override public function get width():Number
		{
			return _video.width;
		}
		
		
		/**
		 * Sets the height of the video player
		 * 
		 * @param 	height	The desired height of the player
		 **/  
		override public function set height(height:Number):void
		{
			if(_lockAspectRatio)
			{
				var ratio:Number = _video.height / height;	
				_video.width /= ratio;	
				_video.height /= ratio;
			}
			else {
				
				_video.height = height;
			}
		}	
		
		
		/**
		 * Returns the height of the video player
		 **/
		override public function get height():Number
		{
			return _video.height;
		}
		
		
		/**
		 *	Locks or unlocks aspect ratio scaling
		 * 
		 * 	@param	value	boolean value to set to lock aspect ratio
		 **/ 
		public function set lockAspectRatio(value: Boolean):void
		{
			_lockAspectRatio = value;
		}
		
		
		/**
		 * Returns whether the aspect ratio is locked
		 **/ 
		public function get lockAspectRatio():Boolean
		{
			return _lockAspectRatio;
		}
		
		
		/**
		 *	Sets whether the video should play immediately after loading
		 * 
		 * 	@param	value	Boolean value to set to autoplay
		 **/ 
		public function set autoPlay(value: Boolean):void
		{
			_autoPlay = value;
		}
		
		
		/**
		 * Returns whether autoplay is set or not
		 **/ 
		public function get autoPlay():Boolean
		{
			return _autoPlay;
		}
		
		
		/**
		 * Returns the total length of the video in seconds
		 **/ 
		public function get duration():Number
		{
			return _duration;
		}
		
		
		/**
		 * Returns the current position of the playhead in seconds
		 **/ 
		public function get position():Number
		{
			return _stream.time;
		}
		
		
		/**
		 *	Sets the volume of the video. Ranges from 0 to 1
		 * 
		 * 	@param	value	Number to set to video players volume
		 **/ 
		public function set volume(value:Number):void
		{
			if(value > 1)
			{
				value = 1;
			}
			else if(value < 0) {
				
				value = 0;
			}
			
			var soundTransform: SoundTransform = _stream.soundTransform;
			soundTransform.volume = value;
			_stream.soundTransform = soundTransform;			
		}
		
		
		/**
		 * Returns the current volume of the video player
		 **/ 
		public function get volume():Number
		{
			return _stream.soundTransform.volume;
		}
		
		
		/**
		 * Sets the amount of video to buffer before playing
		 * 
		 * @param	time	Number in seconds for the video player to buffer
		 **/ 
		public function set bufferTime(time: Number):void
		{
			_stream.bufferTime = time;
		}
		
		
		/**
		 * Returns the time the player will buffer before playing
		 **/ 
		public function get bufferTime():Number
		{
			return _stream.bufferTime;
		}
		
		
		/**
		 * Returns the total amount of bytes in the video
		 **/ 
		public function get bytesTotal():uint
		{
			return _stream.bytesTotal;
		}
		
		
		/**
		 * Returns the amount of bytes the video player has loaded
		 **/ 
		public function get bytesLoaded():uint
		{
			return _stream.bytesLoaded;
		}
		
		/**
		 * Sets the title of the video
		 * 
		 * @param	value	Title of the video
		 **/ 
		public function set title(value:String):void
		{
			_title = value;
		}
		
		/**
		 * Returns the title of the video. The title must be injected into the FLV as xtradata
		 **/
		public function get title():String
		{
			return _title;
		} 
		
		/**
		 * Sets the loop flag so that when the video finishes it either loops or not.
		 *  
		 * @param arg	Boolean value to set whether to loop or not
		 */		
		public function set loop(arg:Boolean):void
		{
			_loop = arg;
		}		
		
		/**
		 * Gets the value of _loop
		 * 
		 * @return	Boolean value to say whether the video player loops 
		 * 
		 */		
		public function get loop():Boolean
		{
			return _loop;
		}
		
		public function get state():String
		{
			return _state;
		}
		
		/******************************************************************************************
		 * CONSTRUCTOR
		 ******************************************************************************************/
		  
		public function VideoPlayer( width:int = 320, height:int = 240, lockAspect:Boolean = true, bufferTime:Number = 5, autoPlay:Boolean = true, loop:Boolean = false)
		{
			_connection = new NetConnection();
			_connection.connect(null);
			
			var clientObject:Object = new Object();
			clientObject.onMetaData = onMetaData;
			//clientObject.onNetStatus = onNetStatus;
			
			_stream = new NetStream(_connection);
			_stream.client = clientObject;
			_stream.addEventListener(NetStatusEvent.NET_STATUS, onNetStatus);
			_video = new Video( width, height);	
			_video.attachNetStream(_stream);
			addChild(_video);
			
			_lockAspectRatio = lockAspect;	
			_duration = 0;	
			_muteVolume = 0;			
			_autoPlay = autoPlay;
			_loop = loop;		
		}
		
		public function load(videoURL:String):void
		{	
			_metaDataFired = false;
			_stream.play(videoURL);			
		}
		
		public function play():void
		{
			_stream.resume();
			_state = PLAYING;
			dispatchEvent(new VideoPlayerEvent(VideoPlayerEvent.PLAY, this));
		}
		
		public function pause():void
		{
			_stream.pause();
			_state = PAUSED;
			dispatchEvent(new VideoPlayerEvent(VideoPlayerEvent.PAUSE, this));
		}
		
		public function togglePause():void
		{			
			
			if(_state == PLAYING)
			{
				pause();
			}
			else
			{
				play();
			}
			
			//_stream.togglePause();

		}
		
		public function stop():void
		{
			_stream.seek(0);
			_stream.pause();
			_state = STOPPED;
			dispatchEvent(new VideoPlayerEvent(VideoPlayerEvent.STOP, this));
		}
		
		public function scrub(percentage:Number):void
		{
			if(percentage > 1)
			{
				percentage = 1;
			}
			else if(percentage < 0)
			{
				percentage = 0;
			}
			
			_stream.seek(percentage * _duration);
		}
		
		public function mute():void
		{			
			if(_stream.soundTransform.volume > 0)
			{
				_muteVolume = _stream.soundTransform.volume;
			}
			
			var soundTransform:SoundTransform = _stream.soundTransform;
			soundTransform.volume = 0;
			_stream.soundTransform = soundTransform;
			
			_muted = true;
		}
		
		public function toggleMute():void
		{
			if(_muted)
			{
				_stream.soundTransform = new SoundTransform(_muteVolume);
				_muted = false;
			}
			else
			{
				mute();
			}
		}
		
		private function onMetaData(data:Object):void
		{
			if(!_metaDataFired)
			{
				_title = data.xtradata == null ? "No Title Information" : data.xtradata;
				
				if(_autoPlay)
				{
					play();
				}
				else
				{
					stop();
				}
				
				_duration = data.duration;
				
				dispatchEvent(new VideoPlayerEvent(VideoPlayerEvent.READY, this));
				dispatchEvent(new VideoPlayerEvent(VideoPlayerEvent.LOADING, this));
			}
			
			addEventListener(Event.ENTER_FRAME, onUpdate);
			
			_metaDataFired = true;
		}
		
		private function onNetStatus(data:Object):void
		{
			//trace("Code: "+data.info.code);
			
			if(data.info.code == "NetStream.Play.Stop")
			{
				dispatchEvent(new VideoPlayerEvent(VideoPlayerEvent.FINISHED, this));
				
				if(_loop)
				{
					_stream.seek(0);
				}				
			}
			else if(data.info.code == "NetStream.Buffer.Empty")
			{
				dispatchEvent(new VideoPlayerEvent(VideoPlayerEvent.BUFFERING_STARTED, this));
			}
			else if(data.info.code == "NetStream.Buffer.Full")
			{
				dispatchEvent(new VideoPlayerEvent(VideoPlayerEvent.PLAY, this));
			}
			else if(data.info.code == "NetStream.Buffer.Flush")
			{
				dispatchEvent(new VideoPlayerEvent(VideoPlayerEvent.BUFFERING_STOPPED, this));
			}
		}
		
		private var _loaded:Boolean = false;
		
		private function onUpdate(event:Event):void
		{
			dispatchEvent(new VideoPlayerEvent(VideoPlayerEvent.UPDATE, this));
			
			if(bytesLoaded <= bytesTotal && !_loaded) 
			{
				//trace("dispatch load");
				
				dispatchEvent(new VideoPlayerEvent(VideoPlayerEvent.LOADING, this));
				
				if(bytesLoaded == bytesTotal)
				{
					_loaded = true;
				}
				
				
			}
		}
		
		public function destroy():void
		{
			removeEventListener(Event.ENTER_FRAME, onUpdate);
			_stream.removeEventListener(NetStatusEvent.NET_STATUS, onNetStatus);
			
			_connection.close();
			_connection = null;
			
			_stream.close();
			_stream = null;
			
			try
			{
				removeChild(_video);
			}
			catch(e:Error){ trace(e);}
			
			_video = null;
		}
		
		public function clear():void
		{
			_video.clear();
		}
	}
}